﻿using System;
using System.Text;
using System.Runtime.InteropServices;


namespace dllLink
{
    internal class GSLFunctions
    {
        internal static gsl_rng mRandomGaussian;
        
        // double gsl_stats_mean (const double data[]
        //	, const size_t stride
        //	, const size_t n);
        [DllImport("libgsl.dll", EntryPoint = "gsl_stats_mean")]
        internal static extern double mean(
            [In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] data,
            [In] int stride,
            [In] int n
        );

        /*********
         * 
         * Fonctions pour le calcul de la covariance
         * 
         */ 
        [DllImport("libgsl.dll", EntryPoint = "gsl_stats_covariance")]
        internal static extern double GSLcovariance(
            [In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] double[] data1,
            [In] int stride1,
            [In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] double[] data2,
            [In] int stride2,
            [In] int n
        );

        internal static double covariance(double[] data1, double[] data2, int stride, int n)
        {
            return GSLcovariance(data1, stride, data2, stride, n);
        }


        /*********
         * 
         * Fonction pour le calcul de la décomposition de cholesky
         * 
         */ 
        [DllImport("libgsl.dll", EntryPoint = "gsl_linalg_cholesky_decomp")]
        internal static extern int cholesky(
            [In,Out] [MarshalAs(UnmanagedType.LPStruct)] gsl_matrix A     
        );


        /*********
        * 
        * Fonctions pour la génération de variables aléatoires gaussiennes
        * 
        */ 
        [DllImport("libgsl.dll", EntryPoint = "gsl_rng_env_setup")]
        internal static extern IntPtr gsl_rng_env_setup();


        [DllImport("libgsl.dll", EntryPoint = "gsl_rng_alloc")]
        internal static extern IntPtr gsl_rng_alloc(
            [In] [MarshalAs(UnmanagedType.LPStruct)] gsl_rng_type T
        );

        [DllImport("libgsl.dll", EntryPoint = "gsl_ran_gaussian")]
        internal static extern double gsl_ran_gaussian(
            [In] [MarshalAs(UnmanagedType.LPStruct)] gsl_rng r,
            [In] double sigma
        );

        [DllImport("libgsl.dll", EntryPoint = "gsl_rng_set")]
        internal static extern double gsl_rng_set(
            [In, Out] [MarshalAs(UnmanagedType.LPStruct)] gsl_rng r,
            [In] ulong sigma
        );

        [DllImport("libgsl.dll", EntryPoint = "gsl_rng_name")]
        internal static extern string gsl_rng_name(
            [In] [MarshalAs(UnmanagedType.LPStruct)] gsl_rng r
        );

        [DllImport("libgsl.dll", EntryPoint = "gsl_rng_types_setup")]
        private static extern IntPtr gsl_rng_types_setup();




        internal static void initSeed()
        {
            gsl_rng_type T;
            gsl_rng r = new gsl_rng();
            IntPtr defaultGenerator = GSLFunctions.gsl_rng_env_setup();
            
            T = new gsl_rng_type();

            Marshal.PtrToStructure(defaultGenerator, T);

            // Allocation de la mémoire pour le générateur
            IntPtr s = GSLFunctions.gsl_rng_alloc(T);
            Marshal.PtrToStructure(s, r);
            // Génération d'une nouvele graine pour le générateur
            ulong rand = Convert.ToUInt64((new Random()).Next());
            GSLFunctions.gsl_rng_set(r, rand);
            GSLFunctions.mRandomGaussian = r;
        }

        internal static double getGaussian(double sigma)
        {
            return gsl_ran_gaussian(mRandomGaussian, sigma);
        }

        internal static double getGaussian()
        {
            try
            {
                return gsl_ran_gaussian(mRandomGaussian, 1);
            }
            catch
            {
                initSeed();
                return getGaussian();
            }
        }
    }
}
